import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;


import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;


public class myproxy2 {
	
	public class commandLineProxy{
		private String[] args;
		public Options options;
		private boolean OptionsConfigure = false;
		private HelpFormatter PrinterFormatter = null;
		private int lport = 2000;
		private String remotIP = "localhost";
		private int rport = 23;
		private CommandLineParser Parser;
		
		public commandLineProxy(String[] argumentos){
			args = argumentos;
			options = new Options();
			PrinterFormatter = new HelpFormatter();
			Parser = new PosixParser();
			OptionsCreator();
		}
		
		private void OptionsCreator(){
			OptionsConfigure = true;
			options.addOption("h", "help", false, "Muestra la ayuda");
			options.addOption("l", "lport", true, "puerto local donde poner a escuchar el programa");
			options.addOption("r", "remotIP", true, "IP remota a donde se conectara el proxy");
			options.addOption("p", "rport", true, "Puerto remoto donde nos conetaremos");
		}
		
		public void PrintCmdLineHelp(){
			PrinterFormatter.printHelp("myproxy", options);
		}
		
		public void CmdAnalisis(){
			Option[] tmpArray = null;
			CommandLine cmd = null;
			try{
				cmd = Parser.parse(options, args);
			}
			catch(ParseException e){
				System.err.format("Error al manejar la linea de comandos %s%n", e.getMessage());
			}
			tmpArray = cmd.getOptions();
			//System.out.format("Tamano %d tiene%n", tmpArray.length, tmpArray);
			for(int i = 0; i < tmpArray.length; i++){
				switch(tmpArray[i].getOpt()){
				case "h":
					PrintCmdLineHelp();
					System.exit(1);
				case "l":
					lport = Integer.parseInt(tmpArray[i].getValue());
					break;
				case "r":
					remotIP = tmpArray[i].getValue();
					break;
				case "p":
					rport = Integer.parseInt(tmpArray[i].getValue());
					break;
				default:
					System.out.println("Comando invalido " + tmpArray[i].getArgName());
				}
			}
		}
		public int getLocalPort(){
			return lport;
		}
		
		public int getRemotePort(){
			return rport;
		}
		
		public String getRemoteIP(){
			return remotIP;
		}
	}

	public class ProxyServer implements Runnable{
		public int localport, remoteport;
		public String remoteip;
		private ServerSocket socketserver = null;
		
		public ProxyServer(int localport, int remoteport, String remoteip){
			this.localport = localport;
			this.remoteport = remoteport;
			this.remoteip = remoteip;
		}
		
		public void CreateListenner(){
			try{
				System.out.println("Creando listener");
				socketserver = new ServerSocket(localport);
				Thread t = new Thread(this);
				t.setPriority(Thread.MIN_PRIORITY);
				t.run();
			}
			catch(IOException e){
				System.err.println("Imposible crear el socket de escucha " + e.getMessage());
			}
		}
		
		@Override
		public void run() {
			Socket RcvConection = null;
			//Socket ConToClient = null;
			System.out.println("Dentro del hilo de escucha");
			try {
				RcvConection = socketserver.accept();
				String ClientInfo = RcvConection.getInetAddress().getHostName();
				System.out.println("Recibiendo conexicon de " + ClientInfo +  " Sobre el puerto " + socketserver.getLocalPort());
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			try {
				Socket ConToClient = new Socket(remoteip, remoteport);
				Muxer s1 = new Muxer(RcvConection, ConToClient);
				Muxer s2 = new Muxer(ConToClient, RcvConection);
				s1.MuxingComunication(s2);
				s2.MuxingComunication(s1);
			} catch (UnknownHostException e) {
				System.err.println("Problemas al conectar con el host " + e.getMessage());
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	public class Muxer implements Runnable{
		private Socket localSocket, RemoteSocket;
		private InputStream from; //Aca leemos
		private OutputStream to; //Aca escribimos
		private Muxer lockMuxer;
		
		public Muxer(Socket local, Socket remote)throws IOException{
			localSocket = local;
			RemoteSocket = remote;
			from = local.getInputStream();
			to = remote.getOutputStream();
		}
		
		public void MuxingComunication(Muxer c1){
			lockMuxer = c1;
			Thread tt = new Thread(c1);
			tt.start();
		}
		
		private void MuxingComunicationClose(){
			lockMuxer = null;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			byte buffer[] = new byte[4080];
			int count;
			while(lockMuxer != null){
				try{
					if((count = from.read(buffer)) < 0){
						break; // Aca cerramos el socket por que no tenemos mas que leer
					}
					to.write(buffer, 0, count);
				}
				catch(IOException e){
					System.err.println("Problemas de i/o  " + e.getMessage());
				}
			}
			try{
				from.close();
				to.flush();
				to.close();
				localSocket.close();
				RemoteSocket.close();
			}catch(IOException e){
				System.err.format("Error al cerrar los descriptiores de i/o %s%n", e.getMessage());
			}
			if(lockMuxer != null)
				MuxingComunicationClose();
		}
		
		
	}
	
	public static void main(String[] args) throws IOException{
		System.out.println("My proxy2");
		myproxy2 px = new myproxy2();
		myproxy2.commandLineProxy cmdline = px.new commandLineProxy(args);
		if(args.length <= 1){
			cmdline.PrintCmdLineHelp();
		}
		cmdline.CmdAnalisis();
		myproxy2.ProxyServer proxyserver = px.new ProxyServer(cmdline.getLocalPort(), cmdline.getRemotePort(), cmdline.getRemoteIP());
		System.out.println("Local port " + proxyserver.localport);
		proxyserver.CreateListenner();
		System.out.println("-----");
	}
}
